[id].vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. <script setup lang="ts" generic="T extends any, O extends any">
  2. import type { RouteLocationRaw } from 'vue-router'
  3. import request from '~/utils/request'
  4. // import { stepsMap } from '~/composables/steps'
  5. import { user } from '~/store/index'
  6. const props = defineProps<{
  7. id: string
  8. }>()
  9. const ykl_id = props.id
  10. const server = (await request({
  11. url: '/yzy/kmksyjlc/detail',
  12. data: {
  13. ykl_id,
  14. },
  15. })).data.one_info
  16. const isCreateUser = server.create_user_id === user.value.user_id
  17. const ykl_lc = Object.assign(JSON.parse(Object.assign(server.ykl_lc)), { ykl_id })
  18. console.log('ykl_lc : ', ykl_lc)
  19. sessionStorage.setItem('ykl_lc', JSON.stringify(ykl_lc))
  20. const stepsReactiveMap = reactive<unknown[][][]>(ykl_lc.processList)
  21. const steps = reactive(
  22. ykl_lc.ykj_yjlc === '1'
  23. ? [// 先上传后划块
  24. {
  25. title: '组卷考试',
  26. children: [
  27. {
  28. title: '出题组卷',
  29. children: [
  30. { title: '章节知识点出题', optional: true, disabled: true, description: '选择相应的章节知识点从题库中选取题目组成试卷', ifCreateUser: true },
  31. { title: '智能出题', optional: true, disabled: true, description: '填写题型数量难易度等信息系统自动生成试卷', ifCreateUser: true },
  32. { title: '附件出题', optional: true, description: '根据上传附件试卷进行考试', ifCreateUser: true },
  33. ],
  34. },
  35. {
  36. title: '预划考号区域',
  37. children: [
  38. { title: '预划考号区域', optional: false, description: '根据试题内容格式制作相应的答题卡样式', ifCreateUser: true },
  39. { title: '考场设置', optional: false, description: '根据学生考场分配情况上传' },
  40. ],
  41. },
  42. {
  43. title: '预划流程完成',
  44. children: [
  45. { title: '预划流程完成', optional: false, description: '试卷内容和答题卡已确认,可进行下一步', ifCreateUser: true },
  46. ],
  47. },
  48. ],
  49. },
  50. {
  51. title: '扫描',
  52. children: [
  53. {
  54. title: '答题卡扫描',
  55. children: [
  56. { title: '答题卡扫描', optional: false, description: '启动客户端扫描学生答题卡并进行识别与上传' },
  57. ],
  58. },
  59. {
  60. title: '压缩包上传情况',
  61. children: [
  62. { title: '压缩包上传情况', optional: false, description: '启动客户端扫描试卷形成压缩包打包上传平台' },
  63. ],
  64. },
  65. {
  66. title: '制作答题卡',
  67. children: [
  68. { title: '制作答题卡', optional: false, description: '根据试题内容格式制作相应的答题卡样式', ifCreateUser: true },
  69. ],
  70. },
  71. ],
  72. },
  73. {
  74. title: '制作扫描处理',
  75. children: [
  76. {
  77. title: '答题卡二次扫描',
  78. children: [
  79. { title: '答题卡二次扫描', optional: false, description: '启动客户端二次扫描批阅试卷' },
  80. ],
  81. },
  82. {
  83. title: '平台接收试卷确认',
  84. children: [
  85. { title: '平台接收试卷确认', optional: false, description: '确认试卷已上传' },
  86. ],
  87. },
  88. ],
  89. },
  90. {
  91. title: '阅卷',
  92. children: [
  93. {
  94. title: '批阅任务分配',
  95. children: [
  96. { title: '批阅任务分配', optional: false, description: '对阅卷老师分配批阅任务', ifCreateUser: true },
  97. ],
  98. },
  99. {
  100. title: '阅卷',
  101. children: [
  102. { title: '阅卷', optional: false, description: '查看批阅进度查看我的批阅任务' },
  103. ],
  104. },
  105. ],
  106. },
  107. {
  108. title: '成绩',
  109. children: [
  110. {
  111. title: '成绩发布',
  112. children: [
  113. { title: '成绩发布', optional: false, description: '成绩汇总发布到分析平台', ifCreateUser: true },
  114. ],
  115. },
  116. {
  117. title: '修改成绩',
  118. children: [
  119. { title: '修改成绩', optional: false, description: '考试成绩发布后三天内可修改', ifCreateUser: true },
  120. ],
  121. },
  122. {
  123. title: '考试关闭',
  124. children: [
  125. { title: '考试关闭', optional: false, description: '最后考试结束关闭考试', ifCreateUser: true },
  126. ],
  127. },
  128. ],
  129. },
  130. ]
  131. : [// 先划块后上传
  132. {
  133. title: '组卷考试',
  134. children: [
  135. {
  136. title: '出题组卷',
  137. children: [
  138. { title: '章节知识点出题', optional: true, disabled: true, description: '选择相应的章节知识点从题库中选取题目组成试卷', ifCreateUser: true },
  139. { title: '智能出题', optional: true, disabled: true, description: '填写题型数量难易度等信息系统自动生成试卷', ifCreateUser: true },
  140. { title: '附件出题', optional: true, description: '根据上传附件试卷进行考试', ifCreateUser: true },
  141. ],
  142. },
  143. {
  144. title: '制作答题卡',
  145. children: [
  146. { title: '制作答题卡', optional: false, description: '根据试题内容格式制作相应的答题卡样式', ifCreateUser: true },
  147. { title: '考场设置', optional: false, description: '根据学生考场分配情况上传' },
  148. ],
  149. },
  150. {
  151. title: '组卷流程完成',
  152. children: [
  153. { title: '组卷流程完成', optional: false, description: '试卷内容和答题卡已确认,可进行下一步', ifCreateUser: true },
  154. ],
  155. },
  156. ],
  157. },
  158. {
  159. title: '扫描',
  160. children: [
  161. {
  162. title: '答题卡扫描',
  163. children: [
  164. { title: '连接扫描仪', optional: false, description: '启动客户端扫描学生答题卡并进行识别与上传' },
  165. ],
  166. },
  167. {
  168. title: '平台接收试卷确认',
  169. children: [
  170. { title: '平台接收试卷确认', optional: false, description: '时间及试题均接收并识别入库完成' },
  171. ],
  172. },
  173. ],
  174. },
  175. {
  176. title: '阅卷',
  177. children: [
  178. {
  179. title: '批阅任务分配',
  180. children: [
  181. { title: '批阅任务分配', optional: false, description: '对阅卷老师分配批阅任务', ifCreateUser: true },
  182. ],
  183. },
  184. {
  185. title: '阅卷',
  186. children: [
  187. { title: '阅卷', optional: false, description: '查看批阅进度查看我的批阅任务' },
  188. ],
  189. },
  190. ],
  191. },
  192. {
  193. title: '成绩',
  194. children: [
  195. {
  196. title: '成绩发布',
  197. children: [
  198. { title: '成绩发布', optional: false, description: '成绩汇总发布到分析平台', ifCreateUser: true },
  199. ],
  200. },
  201. {
  202. title: '修改成绩',
  203. children: [
  204. { title: '修改成绩', optional: false, description: '考试成绩发布后三天内可修改', ifCreateUser: true },
  205. ],
  206. },
  207. {
  208. title: '考试关闭',
  209. children: [
  210. { title: '考试关闭', optional: false, description: '最后考试结束关闭考试', ifCreateUser: true },
  211. ],
  212. },
  213. ],
  214. },
  215. ],
  216. )
  217. let gid = $ref<number>(0)
  218. let pids = $ref<number[]>([])
  219. // const cid = $ref(-1)
  220. function getGid() {
  221. return stepsReactiveMap.findLastIndex(item => item.some(subItem => subItem.some(subSubItem => judgeStepCompleted(subSubItem))))
  222. }
  223. function getPids() {
  224. return stepsReactiveMap.map((item, idx) => (idx < gid ? 1 : 0) + item.findLastIndex(subItem => subItem.some(subSubItem => judgeStepCompleted(subSubItem))))
  225. }
  226. watch(() => stepsReactiveMap, (val) => {
  227. gid = getGid()
  228. pids = getPids()
  229. }, {
  230. immediate: true,
  231. deep: true,
  232. })
  233. let currentStep = $ref<number>(gid)
  234. // 当前步骤的card的ref
  235. const CardsRef = $shallowRef<Array<Array<typeof import('~/components/TheCard.vue')['default']>>>([])
  236. const lineList: any[][][] = []
  237. // onMounted(() => {
  238. // CardsRef.forEach((cards, idx) => {
  239. // if (idx === CardsRef.length - 1)
  240. // return
  241. // cards.forEach((card, idy) => {
  242. // const line = new LeaderLine(
  243. // card.getDom(),
  244. // CardsRef[idx + 1][0].getDom(),
  245. // {
  246. // path: 'grid',
  247. // endPlug: 'behind',
  248. // size: 6,
  249. // startSocket: 'right',
  250. // endSocket: 'left',
  251. // color: stepsReactiveMap[currentStep][idx][idy] ? '#003eee' : '#a3a3a3',
  252. // },
  253. // )
  254. // lineList.push(line)
  255. // })
  256. // })
  257. // })
  258. function handleSwitchCurrentStep(id: number) {
  259. currentStep = id
  260. }
  261. onMounted(() => {
  262. watch(() => currentStep, (val, old) => {
  263. if (old !== undefined) {
  264. console.log('上次线条存在并隐藏')
  265. lineList[old].forEach(lines => lines.forEach(line => line?.hide('none')))
  266. }
  267. if (lineList[val]?.length) {
  268. console.log('这次线条存在并显示')
  269. lineList[val].forEach(lines => lines.forEach(line => line?.show('none')))
  270. }
  271. else {
  272. console.log('这次线条不存在并创建', CardsRef)
  273. nextTick(() => {
  274. console.log('nextTick')
  275. CardsRef.forEach((cards, idx) => {
  276. if (idx === CardsRef.length - 1)
  277. return
  278. cards.forEach((card, idy) => {
  279. if ((!card) || (!CardsRef[idx + 1][0]))
  280. return
  281. try {
  282. const line = new LeaderLine(
  283. card.getDom(),
  284. CardsRef[idx + 1][0].getDom(),
  285. {
  286. path: 'grid',
  287. endPlug: 'behind',
  288. size: 6,
  289. startSocket: 'right',
  290. endSocket: 'left',
  291. color: stepsReactiveMap[currentStep][idx][idy] ? '#003eee' : '#a3a3a3',
  292. },
  293. )
  294. line?.position()
  295. if (stepsReactiveMap[currentStep][idx][idy])
  296. document.querySelectorAll('.leader-line')[line._id as number - 1]?.classList.add('z10')
  297. lineList[val] = lineList[val] || []
  298. lineList[val][idx] = lineList[val][idx] || []
  299. lineList[val][idx][idy] = line
  300. console.log('线条绘制成功', lineList)
  301. }
  302. catch (error) {
  303. console.error('线条绘制失败, error')
  304. }
  305. })
  306. })
  307. })
  308. }
  309. }, {
  310. immediate: true,
  311. })
  312. })
  313. const router = useRouter()
  314. function routerPush(path: RouteLocationRaw) {
  315. router.push(path)
  316. }
  317. function windowPushState(path: string) {
  318. // ! 未启用
  319. sessionStorage.setItem('historyLocation', location.href)
  320. window.open(`${location.origin}/${path}`, '_self')
  321. // location.replace(`${location.origin}/${path}`)
  322. // window.history.pushState(null, '', `${location.origin}/${path}`)
  323. }
  324. // 采取如下方式主要由于chrome下history.back()不会重新load页面
  325. window.addEventListener('pageshow', (e) => {
  326. if (e.persisted)
  327. window.location.reload()
  328. })
  329. function handleValidTask(currentStep: number, idx: number, idy: number) {
  330. let lastIdx = idx
  331. let lastStep = currentStep
  332. const lastTasks = idx > 0 ? stepsReactiveMap[currentStep][lastIdx = idx - 1] : currentStep > 0 ? stepsReactiveMap[lastStep = currentStep - 1][lastIdx = stepsReactiveMap[currentStep - 1].length - 1] : []
  333. if (lastTasks.every((item, idz) => {
  334. if (steps[lastStep].children[lastIdx].children[idz].optional)
  335. return true
  336. else
  337. return !!item
  338. })) {
  339. sessionStorage.setItem('StepId', JSON.stringify({ gid: currentStep, pid: idx, cid: idy }))
  340. return true
  341. }
  342. else {
  343. ElMessage({
  344. message: '请先完成之前的任务',
  345. type: 'warning',
  346. grouping: true,
  347. })
  348. return false
  349. }
  350. }
  351. function getArrayLastElement<T>(arr: T[]) {
  352. return arr[arr.length - 1]
  353. }
  354. function judgeIfContinueDoTask(gid: number, pid: number, idy: number, ifMessage: boolean, ifUselessWhenStepEnd: boolean /* 考试关闭后禁用 */) {
  355. const currentTask = steps[gid].children[pid].children[idy]
  356. const continueDoTaskByUser = !currentTask.ifCreateUser || isCreateUser
  357. if (!continueDoTaskByUser) {
  358. ifMessage && ElMessage({
  359. message: '只有考试创建人才能操作',
  360. type: 'warning',
  361. grouping: true,
  362. })
  363. return false
  364. }
  365. if (ifUselessWhenStepEnd) {
  366. if (judgeStepCompleted(getArrayLastElement(getArrayLastElement(stepsReactiveMap))[0])) {
  367. ifMessage && ElMessage({
  368. message: '考试已关闭,无法操作',
  369. type: 'warning',
  370. grouping: true,
  371. })
  372. return false
  373. }
  374. }
  375. return true
  376. }
  377. const ifUselessWhenStepEndTaskList = ['修改成绩', '考试关闭']
  378. function beforeClickTask(gid: number, pid: number, idy: number) {
  379. const currentTask = steps[gid].children[pid].children[idy]
  380. const continueDoTask = judgeIfContinueDoTask(gid, pid, idy, true, ifUselessWhenStepEndTaskList.includes(currentTask.title))
  381. if (!continueDoTask)
  382. return false
  383. else
  384. return handleValidTask(gid, pid, idy)
  385. }
  386. function judgeTaskCanClick(gid: number, pid: number, idy: number, ifUselessWhenStepEnd?: boolean /* 考试关闭后是否禁用 */) {
  387. const continueDoTask = judgeIfContinueDoTask(gid, pid, idy, false, ifUselessWhenStepEnd)
  388. if (!continueDoTask) {
  389. return false
  390. }
  391. else {
  392. let lastIdx = pid
  393. let lastStep = currentStep
  394. const lastTasks = pid > 0 ? stepsReactiveMap[currentStep][lastIdx = pid - 1] : currentStep > 0 ? stepsReactiveMap[lastStep = currentStep - 1][lastIdx = stepsReactiveMap[currentStep - 1].length - 1] : []
  395. if (lastTasks.every((item, idz) => {
  396. if (steps[lastStep].children[lastIdx].children[idz].optional)
  397. return true
  398. else
  399. return !!item
  400. }))
  401. return true
  402. else
  403. return false
  404. }
  405. }
  406. function judgeStepCompleted(val: unknown) {
  407. return (!!val) || (typeof val === 'object' && val !== null && !!(val?.value))
  408. }
  409. function handleCompleteTask(gid: number, pid: number, cid: number, val?: unknown, ext?: any): Promise<any> {
  410. const _ykl_lc_ = JSON.parse(sessionStorage.getItem('ykl_lc')!)
  411. _ykl_lc_.processList[gid][pid][cid] = val || 1
  412. const reqData: any = {
  413. ykl_id,
  414. yk: {
  415. ykl_lc: JSON.stringify(_ykl_lc_),
  416. },
  417. }
  418. // 把ext上的属性合并到reqData上,考虑嵌套的情况
  419. if (ext) {
  420. Object.keys(ext).forEach((key) => {
  421. if (typeof ext[key] === 'object') {
  422. if (reqData[key]) {
  423. reqData[key] = {
  424. ...reqData[key],
  425. ...ext[key],
  426. }
  427. }
  428. else { reqData[key] = ext[key] }
  429. }
  430. else {
  431. reqData[key] = ext[key]
  432. }
  433. })
  434. }
  435. return request({
  436. url: '/yzy/kmksyjlc/save',
  437. data: reqData,
  438. }).then((res) => {
  439. if (res.code === '1') {
  440. ElMessage({
  441. message: '操作成功',
  442. type: 'success',
  443. grouping: true,
  444. })
  445. sessionStorage.setItem('ykl_lc', JSON.stringify(_ykl_lc_))
  446. stepsReactiveMap[gid][pid][cid] = val || 1
  447. return res
  448. }
  449. }).catch(() => {
  450. ElMessage({
  451. message: '操作失败',
  452. type: 'error',
  453. grouping: true,
  454. })
  455. })
  456. }
  457. async function handleJumpTask(gid: number, pid: number, idy: number, ext?: object) {
  458. // 验证任务是否完成
  459. if (judgeStepCompleted(stepsReactiveMap[gid][pid][idy])) {
  460. return ElMessage({
  461. message: '该任务已完成',
  462. type: 'warning',
  463. grouping: true,
  464. })
  465. }
  466. // stepsReactiveMap[gid][pid][idy] = 1
  467. const res = await handleCompleteTask(gid, pid, idy, 1, ext)
  468. if (res.code === '1') {
  469. if (pid !== stepsReactiveMap[gid].length - 1) {
  470. try {
  471. const line = lineList[gid][pid][idy]
  472. line.setOptions({ color: '#003eee' })
  473. document.querySelectorAll('.leader-line')[line._id - 1]?.classList.add('z10')
  474. }
  475. catch (error) {
  476. console.error('线条更新失败', error)
  477. }
  478. }
  479. }
  480. return res
  481. }
  482. onBeforeRouteLeave(() => {
  483. try {
  484. lineList.forEach(lines => lines.forEach(line => line.forEach(l => l?.hide('none'))))
  485. }
  486. catch (error) {
  487. console.error('线条卸载失败', error)
  488. }
  489. })
  490. function handleCompleteTaskAuto(ext?: object) {
  491. const { gid, pid, cid } = JSON.parse(sessionStorage.getItem('StepId')!)
  492. return handleJumpTask(gid, pid, cid, ext)
  493. }
  494. const TaskEventMap: { [key: string]: () => void } = {
  495. 章节知识点出题: () => {
  496. ElMessage.info('暂未开放')
  497. },
  498. 智能出题: () => {
  499. ElMessage.info('暂未开放')
  500. },
  501. 附件出题: () => {
  502. routerPush({ name: 'process-fjct-ze_id-zs_id', params: { ze_id: ykl_lc.ze_id, zs_id: ykl_lc.zs_id } })
  503. },
  504. 预划考号区域: () => {
  505. windowPushState(`${window.GLOBAL_CONFIG.build}/dtk/index.html`)
  506. },
  507. 制作答题卡: () => {
  508. windowPushState(`${window.GLOBAL_CONFIG.build}/dtk/index.html`)
  509. },
  510. 考场设置: () => {
  511. windowPushState(`${window.GLOBAL_CONFIG.base}/exam-room-set.html?id=${ykl_lc.ze_id}`)
  512. },
  513. 预划流程完成: async () => {
  514. await handleCompleteTaskAuto()
  515. currentStep = 1
  516. },
  517. 组卷流程完成: async () => {
  518. await handleCompleteTaskAuto()
  519. currentStep = 1
  520. },
  521. // 先上传后划块
  522. 答题卡扫描: () => {
  523. window.open(`BozeduYuejuan://${user.value.token},${ykl_lc.ze_id},${window.GLOBAL_CONFIG.yzy},upload_papers`, '_blank')
  524. handleCompleteTaskAuto()
  525. },
  526. 压缩包上传情况: async () => {
  527. await handleCompleteTaskAuto()
  528. // TODO: 需调整【压缩包】上传情况
  529. routerPush(`/process/ysb/${ykl_lc.ykl_id}/${ykl_lc.ze_id}`)
  530. },
  531. 答题卡二次扫描: () => {
  532. window.open(`BozeduYuejuan://${user.value.token},${ykl_lc.ze_id},${window.GLOBAL_CONFIG.yzy},review_papers`, '_blank')
  533. handleCompleteTaskAuto()
  534. },
  535. // 先划块后上传 老流程
  536. 连接扫描仪: () => {
  537. window.open(`BozeduYuejuan://${user.value.token},${ykl_lc.ze_id},${window.GLOBAL_CONFIG.yzy}`, '_blank')
  538. handleCompleteTaskAuto()
  539. },
  540. 平台接收试卷确认: async () => {
  541. await handleCompleteTaskAuto()
  542. windowPushState(`${window.GLOBAL_CONFIG.base}/marking-answer-sheet-liankao.html?ze_id=${ykl_lc.ze_id}`)
  543. },
  544. 批阅任务分配: () => {
  545. windowPushState(`${window.GLOBAL_CONFIG.base}/taskassignment-liankao.html`)
  546. },
  547. 阅卷: async () => {
  548. await handleCompleteTaskAuto()
  549. // todo: 需调整【阅卷】
  550. windowPushState(`${window.GLOBAL_CONFIG.base}/single-review-liankao.html?ze_id=${ykl_lc.ze_id}`)
  551. },
  552. 成绩发布: () => {
  553. let loading = false
  554. ElMessageBox.confirm('选择实时发布时需等待浏览器处理结束;正常发布则无需等待', '选择发布成绩的方式', {
  555. confirmButtonText: '实时发布',
  556. cancelButtonText: '正常发布',
  557. type: 'info',
  558. distinguishCancelAndClose: true,
  559. beforeClose: async (action, instance, done) => {
  560. if (action === 'confirm') {
  561. // 实时发布
  562. console.log('实时发布')
  563. instance.confirmButtonLoading = loading = true
  564. try {
  565. const res0 = await request({
  566. url: '/openapi/crontab/datamaker_v2.php',
  567. data: {
  568. ykl_id,
  569. },
  570. })
  571. if (res0.code !== '1')
  572. throw new Error(res0.msg)
  573. const res1 = await handleCompleteTaskAuto({ force: 1 })
  574. if (res1.code !== '1')
  575. throw new Error(res1.msg)
  576. ykl_lc.ykl_stat_ready = 2
  577. ElMessage.success('发布成功')
  578. }
  579. catch (error) {
  580. console.error('发布失败', error)
  581. }
  582. instance.confirmButtonLoading = loading = false
  583. }
  584. else if (action === 'cancel') {
  585. // 正常发布
  586. console.log('正常发布')
  587. instance.cancelButtonLoading = loading = true
  588. try {
  589. // const res0 = await request({
  590. // url: '/yzy/kmksyjlc/save',
  591. // data: {
  592. // ykl_id,
  593. // yk: {
  594. // ykl_sffbcj: 1,
  595. // },
  596. // },
  597. // })
  598. // if (res0.code !== '1')
  599. // throw new Error(res0.msg)
  600. const res1 = await handleCompleteTaskAuto({
  601. force: 1,
  602. yk: {
  603. ykl_sffbcj: 1,
  604. },
  605. })
  606. if (res1.code !== '1')
  607. throw new Error(res1.msg)
  608. ykl_lc.ykl_stat_ready = 1
  609. ElMessage.success('发布中...')
  610. }
  611. catch (error) {
  612. console.error('发布失败', error)
  613. }
  614. instance.cancelButtonLoading = loading = false
  615. }
  616. else {
  617. console.log('取消发布')
  618. return loading ? ElMessage.warning('发布中,请勿关闭弹窗') : done()
  619. }
  620. },
  621. })
  622. },
  623. 修改成绩: async () => {
  624. await handleCompleteTaskAuto({ force: 1 })
  625. routerPush({ name: 'process-xgcj-id', params: { id: ykl_lc.ze_id } })
  626. },
  627. 考试关闭: () => {
  628. ElMessageBox({
  629. title: '提示',
  630. message: '确定要关闭考试吗?',
  631. type: 'warning',
  632. showCancelButton: true,
  633. confirmButtonText: '确定',
  634. cancelButtonText: '取消',
  635. }).then(() => {
  636. handleCompleteTaskAuto({ force: 1 })
  637. })
  638. },
  639. }
  640. </script>
  641. <template>
  642. <div class="w-full min-h-screen flex flex-col bg-white">
  643. <NavHeader />
  644. <bread-crumb />
  645. <div class="flex-auto w-1200px m-auto">
  646. <div class="flex flex-auto justify-start divide-x-4 divide-blue-700">
  647. <div class="h-full w-220px">
  648. <el-steps direction="vertical" :active="currentStep">
  649. <el-step
  650. v-for="({ title, children }, idx) in steps" :key="title" class="cursor-pointer hover:bg-light-600"
  651. :class="idx === currentStep && 'bg-light-400'" @click="handleSwitchCurrentStep(idx)"
  652. >
  653. <template #title>
  654. <div class="mb-4 font-normal">
  655. {{ title }}
  656. </div>
  657. </template>
  658. <template #description>
  659. <el-steps direction="vertical" :space="40" :active="pids[idx]">
  660. <el-step v-for="({ title }) in children" :key="title">
  661. <template #title>
  662. <div class="font-normal">
  663. {{ title }}
  664. </div>
  665. </template>
  666. </el-step>
  667. </el-steps>
  668. </template>
  669. </el-step>
  670. </el-steps>
  671. </div>
  672. <div class="flex-auto p-4">
  673. <div class="h-full flex justify-between">
  674. <div
  675. v-for="(step, idx) in steps[currentStep].children " :key="idx"
  676. class="h-full flex flex-col justify-evenly"
  677. >
  678. <the-card
  679. v-for="(task, idy) in step.children " :key="idy"
  680. :ref="el => { CardsRef[idx] = CardsRef[idx] || []; CardsRef[idx][idy] = el as any }" :title="task.title"
  681. :description="task.description" :completed="judgeStepCompleted(stepsReactiveMap[currentStep][idx][idy])"
  682. >
  683. <template #operate>
  684. <template v-if="task.title === '预划考号区域'">
  685. <div
  686. class="min-w-60px cursor-pointer rounded bg-light-50 py-3px text-center text-xs"
  687. @click="beforeClickTask(currentStep, idx, idy) && TaskEventMap[task.title]()"
  688. >
  689. {{ judgeStepCompleted(stepsReactiveMap[currentStep][idx][idy]) ? '已经完成' : '立即开始' }}
  690. </div>
  691. <div
  692. class="min-w-60px cursor-pointer rounded bg-light-50 py-3px text-center text-xs"
  693. @click="beforeClickTask(currentStep, idx, idy) && handleJumpTask(currentStep, idx, idy)"
  694. >
  695. {{ '跳过' }}
  696. </div>
  697. </template>
  698. <template v-else-if="task.title === '成绩发布'">
  699. <div
  700. class="min-w-60px cursor-pointer rounded bg-light-50 py-3px text-center text-xs"
  701. @click="beforeClickTask(currentStep, idx, idy) && (TaskEventMap[task.title] ? TaskEventMap[task.title]() : handleCompleteTaskAuto())"
  702. >
  703. {{ judgeStepCompleted(stepsReactiveMap[currentStep][idx][idy])
  704. ? (ykl_lc.ykl_stat_ready === 1 ? '发布中…' : '已发布') : '成绩发布' }}
  705. </div>
  706. </template>
  707. <template v-else>
  708. <div
  709. class="min-w-60px rounded bg-light-50 py-3px text-center text-xs"
  710. :class="task.disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
  711. @click="beforeClickTask(currentStep, idx, idy) && (TaskEventMap[task.title] ? TaskEventMap[task.title]() : handleCompleteTaskAuto())"
  712. >
  713. {{ judgeStepCompleted(stepsReactiveMap[currentStep][idx][idy]) ? '已经完成' : task.disabled ? '暂未开放'
  714. : '立即开始'
  715. }}
  716. </div>
  717. </template>
  718. </template>
  719. <template #tip>
  720. <template v-if="task.title === '成绩发布'">
  721. <div class="mt-2 flex flex-col items-start px-1 text-xs space-y-1" style="--el-font-size-base:10px">
  722. <xgstda :disabled="!judgeTaskCanClick(currentStep, idx, idy, true)" />
  723. <szcjckqx :disabled="!judgeTaskCanClick(currentStep, idx, idy, false)" />
  724. <bjqk :disabled="!judgeTaskCanClick(currentStep, idx, idy, true)" />
  725. </div>
  726. </template>
  727. </template>
  728. </the-card>
  729. </div>
  730. </div>
  731. </div>
  732. </div>
  733. </div>
  734. <commonFooter />
  735. </div>
  736. </template>
  737. <style scoped>
  738. :deep(.el-step:last-of-type .el-step__line) {
  739. display: block;
  740. }
  741. :deep(.el-step__description .el-step:last-of-type .el-step__line) {
  742. display: none;
  743. }
  744. </style>
  745. <route>
  746. {
  747. meta: {
  748. title: '考试流程',
  749. breadcrumb: true,
  750. },
  751. }
  752. </route>